package com.siashan.unipay4j.wx.pay;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.siashan.unipay4j.core.IPayConfig;
import com.siashan.unipay4j.core.PayChannel;
import com.siashan.unipay4j.core.Result;
import com.siashan.unipay4j.core.UnipayConst;
import com.siashan.unipay4j.core.pay.IRefund;
import com.siashan.unipay4j.core.pay.RefundModel;
import com.siashan.unipay4j.core.pay.RefundQueryModel;
import com.siashan.unipay4j.core.pay.RefundResponse;
import com.siashan.unipay4j.core.util.EnumUtil;
import com.siashan.unipay4j.core.util.MoneyUtil;
import com.siashan.unipay4j.core.util.RequestUtil;
import com.siashan.unipay4j.core.util.StrUtil;
import com.siashan.unipay4j.wx.AbstractWxPay;
import com.siashan.unipay4j.wx.CurrencyEnum;
import com.siashan.unipay4j.wx.WxPayVersionEnum;
import com.siashan.unipay4j.wx.WxRefundStateEnum;
import com.siashan.unipay4j.wx.model.refund.*;
import com.siashan.unipay4j.wx.sdk.WXPayConstants;
import com.siashan.unipay4j.wx.sdk.WXPayUtil;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.constant.WechatPayHttpHeaders;
import com.wechat.pay.contrib.apache.httpclient.notification.Notification;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;

import javax.servlet.http.HttpServletRequest;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

import static org.apache.http.HttpStatus.SC_MULTIPLE_CHOICES;
import static org.apache.http.HttpStatus.SC_OK;

/**
 * 微信支付退款
 * <p>退款</p>
 * 当交易发生之后一段时间内，由于买家或者卖家的原因需要退款时，卖家可以通过退款接口将支付款退还给买家，微信支付将在收到退款请求并且验证成功之后，按照退款规则将支付款按原路退到买家帐号上。
 * <p>
 * 注意：
 * <p>
 * 1、交易时间超过一年的订单无法提交退款
 * <p>
 * 2、微信支付退款支持单笔交易分多次退款，多次退款需要提交原支付订单的商户订单号和设置不同的退款单号。申请退款总金额不能超过订单金额。 一笔退款失败后重新提交，请不要更换退款单号，请使用原商户退款单号
 * <p>
 * 3、请求频率限制：150qps，即每秒钟正常的申请退款请求次数不超过150次错误或无效请求频率限制：6qps，即每秒钟异常或错误的退款申请请求不超过6次
 * <p>
 * 4、每个支付订单的部分退款次数不能超过50次
 * <p>
 * 5、如果同一个用户有多笔退款，建议分不同批次进行退款，避免并发退款导致退款失败
 * 6、申请退款接口的返回仅代表业务的受理情况，具体退款是否成功，需要通过退款查询接口获取结果
 * <p>
 * 7、一个月之前的订单申请退款频率限制为：5000/min
 * <p>
 * 8、同一笔订单多次退款的请求需相隔1分钟
 *
 * @author siashan
 * @since v1.0.1
 **/
@Slf4j
public class WxPayRefund extends AbstractWxPay implements IRefund {

    public WxPayRefund() {
        super();
    }

    public WxPayRefund(IPayConfig payConfig) {
        super(payConfig);
    }

    /**
     * 退款请求
     * 申请退款接口的返回仅代表业务的受理情况，具体退款是否成功，需要通过退款查询接口获取结果
     *
     * @param model 退款请求参数
     * @return 退款结果
     */
    @Override
    public Result<RefundResponse> execRefund(RefundModel model) {
        WxPayVersionEnum ver = getWxPayVersion();
        Result result = null;
        switch (ver) {
            case V2:
                result = doRefundV2(model);
                break;
            case V3:
                result = doRefundV3(model);
                break;
            default:
                break;
        }
        return result;
    }


    /**
     * 退款请求 V2
     * <p>
     * 申请退款接口的返回仅代表业务的受理情况，具体退款是否成功，需要通过退款查询接口获取结果
     *
     * @param model 退款请求参数
     * @return 退款结果
     */
    public Result<RefundResponse> doRefundV2(RefundModel model) {
        log.info("WxRefund::=>退款请求到达,退款单号{}", model.getOutRefundNo());
        try {
            Map<String, String> r = initWxPay().refund(wrapRefundParam(model));
            String returnCode = r.get(WXPayConstants.RETURN_CODE);
            if (isReturnSuccess(returnCode)) {
                String resultCode = r.get(WXPayConstants.RESULT_CODE);
                if (UnipayConst.SUCCESS.equals(resultCode)) {
                    log.info("WxRefund::=>退款提交业务成功,退款单号{}", model.getOutRefundNo());
                    return Result.tradeSuccess(wrapperRefundResponse(r));
                } else {
                    String errCode = r.get(WXPayConstants.ERR_CODE);
                    String errCodeDes = r.get(WXPayConstants.ERR_CODE_DES);
                    log.info("WxRefund::=>退款提交业务失败,退款单号{}，错误代码{}，错误原因{}", model.getOutRefundNo(), errCode, errCodeDes);
                    return Result.tradeFail(errCode, errCodeDes);
                }
            } else {
                String returnMsg = r.get(WXPayConstants.RETURN_MSG);
                log.info("WxRefund::=>退款失败,退款单号{}，错误代码{}，错误原因{}", model.getOutRefundNo(), returnCode, returnMsg);
                return Result.returnFail(returnCode, returnMsg);
            }
        } catch (Exception e) {
            log.warn("WxRefund::=>退款异常,退款单号{}", model.getOutRefundNo(), e);
        }
        return Result.exception();
    }

    /**
     * 退款请求 V3
     * <p>
     * 申请退款接口的返回仅代表业务的受理情况，具体退款是否成功，需要通过退款查询接口获取结果
     *
     * @param model 退款请求参数
     * @return 退款结果
     */
    public Result<RefundResponse> doRefundV3(RefundModel model) {
        log.info("WxRefund::=>退款请求到达,退款单号{}", model.getOutRefundNo());
        ApplyRefundModel refundModel = wapperApplyRefundModel(model);
        WechatPayHttpClientBuilder builder = loadBuilder();
        // ... 接下来，你仍然可以通过builder设置各种参数，来配置你的HttpClient
        // 通过WechatPayHttpClientBuilder构造的HttpClient，会自动的处理签名和验签
        CloseableHttpClient httpClient = builder.build();
        HttpPost httpPost = initHttpPost(WXPayConstants.V3_PAY_URL_REFUND, JSON.toJSONString(refundModel));
        try {
            CloseableHttpResponse response = httpClient.execute(httpPost);
            StatusLine statusLine = response.getStatusLine();
            String body = EntityUtils.toString(response.getEntity());
            if (statusLine.getStatusCode() >= SC_OK && statusLine.getStatusCode() < SC_MULTIPLE_CHOICES) {
                log.info("WxRefund::=>退款提交业务成功,退款单号{}", model.getOutRefundNo());
                ApplyRefundResponse responseModel = JSON.parseObject(body, ApplyRefundResponse.class);
                return Result.tradeSuccess(wrapperRefundResponseV3(responseModel));
            } else {
                JSONObject bodyJson = JSON.parseObject(body);
                String errCode = bodyJson.getString(WXPayConstants.CODE);
                String errCodeDes = bodyJson.getString(WXPayConstants.MESSAGE);
                log.info("WxRefund::=>退款提交业务失败,退款单号{}，错误代码{}，错误原因{}", model.getOutRefundNo(), errCode, errCodeDes);
                return Result.tradeFail(errCode, errCodeDes);
            }
        } catch (Exception e) {
            log.warn("WxRefund::=>退款异常,退款单号{}", model.getOutRefundNo(), e);
        }
        return Result.exception();
    }


    /**
     * 退款查询
     * <p>
     * 仅支持根据退款单号查询单笔退款是否成功，不支持根据交易流水号查询批量退款结果
     *
     * @param model 退款查询参数
     * @return 退款查询结果
     */
    @Override
    public Result<RefundResponse> execQuery(RefundQueryModel model) {
        WxPayVersionEnum ver = getWxPayVersion();
        Result result = null;
        switch (ver) {
            case V2:
                result = execQueryV2(model);
                break;
            case V3:
                result = execQueryV3(model);
                break;
            default:
                break;
        }
        return result;
    }

    /**
     * 退款查询
     * <p>
     * 仅支持根据退款单号查询单笔退款是否成功，不支持根据交易流水号查询批量退款结果
     *
     * @param model 退款查询参数
     * @return 退款查询结果
     */
    public Result<RefundResponse> execQueryV2(RefundQueryModel model) {
        log.info("WxRefund::=>退款查询请求到达,退款单号{}", model.getOutRefundNo());
        try {
            Map<String, String> r = initWxPay().refundQuery(wrapQueryParam(model));
            String returnCode = r.get(WXPayConstants.RETURN_CODE);
            if (isReturnSuccess(returnCode)) {
                String resultCode = r.get(WXPayConstants.RESULT_CODE);
                if (isResultSuccess(resultCode)) {
                    String refund_status_0 = r.get("refund_status_0");
                    WxRefundStateEnum refundState = EnumUtil.getIEnum(WxRefundStateEnum.class, refund_status_0);
                    return Result.tradeSuccess(wrapperRefundResponse(r, refundState, false));
                } else {
                    String errCode = r.get(WXPayConstants.ERR_CODE);
                    String errCodeDes = r.get(WXPayConstants.ERR_CODE_DES);
                    log.info("WxRefund::=>退款查询请求失败,退款单号{}，错误代码{}，错误原因{}", model.getOutRefundNo(), errCode, errCodeDes);
                    return Result.tradeFail(errCode, errCodeDes);
                }
            } else {
                String returnMsg = r.get(WXPayConstants.RETURN_MSG);
                log.info("WxRefund::=>退款查询失败,退款单号{}，错误代码{}，错误原因{}", model.getOutRefundNo(), returnCode, returnMsg);
                return Result.returnFail(returnCode, returnMsg);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Result.exception();
    }

    /**
     * 退款查询
     * <p>
     * 仅支持根据退款单号查询单笔退款是否成功，不支持根据交易流水号查询批量退款结果
     *
     * @param model 退款查询参数
     * @return 退款查询结果
     */
    public Result<RefundResponse> execQueryV3(RefundQueryModel model) {
        log.info("WxRefundQuery::=>退款查询请求到达,退款单号{}", model.getOutRefundNo());
        // 如果微信支付平台返回的交易号不为空，优先根据微信支付交易号查询，如果为空则根据平台交易号查询
        WechatPayHttpClientBuilder builder = loadBuilder();
        CloseableHttpClient httpClient = builder.build();
        HttpGet httpGet = initHttpGet(model.getOutRefundNo());
        try {
            CloseableHttpResponse response = httpClient.execute(httpGet);
            StatusLine statusLine = response.getStatusLine();
            String body = EntityUtils.toString(response.getEntity());
            log.info("WxRefundQuery::=>退款查询，退款单号{},返回报文，{}", model.getOutRefundNo(), body);
            if (statusLine.getStatusCode() >= SC_OK && statusLine.getStatusCode() < SC_MULTIPLE_CHOICES) {
                ApplyRefundResponse responseModel = JSON.parseObject(body, ApplyRefundResponse.class);
                return Result.tradeSuccess(wrapperRefundResponseV3(responseModel));
            } else {
                JSONObject bodyJson = JSON.parseObject(body);
                String errCode = bodyJson.getString(WXPayConstants.CODE);
                String errCodeDes = bodyJson.getString(WXPayConstants.MESSAGE);
                log.info("WxRefundQuery::=>退款查询，退款单号{},查询失败,错误代码{},错误原因{}", model.getOutRefundNo(), errCode, errCodeDes);
                return Result.returnFail(errCode, errCodeDes);
            }
        } catch (Exception e) {
            log.warn("WxRefundQuery=>退款查询，退款单号{} 查询时发生异常", model.getOutRefundNo(), e);
        }
        return Result.exception();
    }
    private HttpGet initHttpGet(String outRefundNo) {

        try {
            URIBuilder uriBuilder = new URIBuilder(wrapOutRefundNoQueryUrl(outRefundNo));
            HttpGet httpGet = new HttpGet(uriBuilder.build());
            return httpGet;
        } catch (URISyntaxException e) {
            log.warn("构建退款查询请求Url异常", e);
            throw new RuntimeException();
        }
    }

    private String wrapOutRefundNoQueryUrl(String out_refund_no) {
        return WXPayConstants.V3_PAY_URL_REFUND_QUERY.replace("{out_refund_no}", out_refund_no.trim());
    }


    /**
     * 退款结果通知
     * <p>
     *
     * @param request 回调请求
     * @return 退款回调结果
     */
    @Override
    public Result<RefundResponse> execNotify(HttpServletRequest request) {
        WxPayVersionEnum ver = getWxPayVersion();
        Result result = null;
        switch (ver) {
            case V2:
                result = execNotifyV2(request);
                break;
            case V3:
                result = execNotifyV3(request);
                break;
            default:
                break;
        }
        return result;
    }

    /**
     * 退款结果通知
     * <p>
     * 应用场景<br>
     * 当商户申请的退款有结果后（退款状态为：退款成功、退款关闭、退款异常），微信会把相关结果发送给商户，商户需要接收处理，并返回应答。
     * 对后台通知交互时，如果微信收到商户的应答不是成功或超时，微信认为通知失败，微信会通过一定的策略定期重新发起通知，尽可能提高通知的成功率，
     * 但微信不保证通知最终能成功（通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m）。
     * 注意：同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。
     * 推荐的做法是，当收到通知进行处理时，首先检查对应业务数据的状态，判断该通知是否已经处理过，如果没有处理过再进行处理，如果处理过直接返回结果成功。
     * 在对业务数据进行状态检查和处理之前，要采用数据锁进行并发控制，以避免函数重入造成的数据混乱。
     * 特别说明：退款结果对重要的数据进行了加密，商户需要用商户密钥进行解密后才能获得结果通知的内容
     * <p>
     * 解密方式<br>
     * 解密步骤如下：
     * （1）对加密串A做base64解码，得到加密串B
     * （2）对商户key做md5，得到32位小写key* ( key设置路径：微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置 )
     * <p>
     * （3）用key*对加密串B做AES-256-ECB解密（PKCS7Padding）
     * <p>
     * 接口链接<br>
     * 在申请退款接口中上传参数“notify_url”以开通该功能
     * 如果链接无法访问，商户将无法接收到微信通知。
     * 通知url必须为直接可访问的url，不能携带参数。公网域名必须为https，如果是走专线接入，使用专线NAT IP或者私有回调域名可使用http。
     * 示例：notify_url：“https://pay.weixin.qq.com/wxpay/pay.action”
     *
     * @param request Request请求
     * @return 通知结果
     */
    public Result<RefundResponse> execNotifyV2(HttpServletRequest request) {
        try {
            String xmlStr = RequestUtil.getStrFromInputSteam(request.getInputStream());
            Map<String, String> map = WXPayUtil.xmlToMap(xmlStr);
            String returnCode = map.get(WXPayConstants.RETURN_CODE);
            if (isReturnSuccess(returnCode)) {
                // 加密字段
                String reqInfo = map.get("req_info");
                String reqInfoXml = WXPayUtil.decryptRefundReqInfo(reqInfo, wxPayConfig.getKey());
                Map<String, String> r = WXPayUtil.xmlToMap(reqInfoXml);
                String refundStatus = r.get("refund_status");
                WxRefundStateEnum wxRefundState = EnumUtil.getIEnum(WxRefundStateEnum.class, refundStatus);
                return Result.tradeSuccess(wrapperRefundResponse(r, wxRefundState, false));
            } else {
                // 通信失败
                return Result.returnFail(returnCode, map.get(WXPayConstants.RETURN_MSG));
            }
            // 解析微信回调参数
        } catch (Exception e) {
            log.warn("WxRefundNotify::=> 回调时发生异常", e);
        }
        return Result.exception();
    }

    /**
     * 退款结果通知
     * <p>
     * 应用场景<br>
     * 当商户申请的退款有结果后（退款状态为：退款成功、退款关闭、退款异常），微信会把相关结果发送给商户，商户需要接收处理，并返回应答。
     * 对后台通知交互时，如果微信收到商户的应答不是成功或超时，微信认为通知失败，微信会通过一定的策略定期重新发起通知，尽可能提高通知的成功率，
     * 但微信不保证通知最终能成功（通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m）。
     * 注意：同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。
     * 推荐的做法是，当收到通知进行处理时，首先检查对应业务数据的状态，判断该通知是否已经处理过，如果没有处理过再进行处理，如果处理过直接返回结果成功。
     * 在对业务数据进行状态检查和处理之前，要采用数据锁进行并发控制，以避免函数重入造成的数据混乱。
     * 特别说明：退款结果对重要的数据进行了加密，商户需要用商户密钥进行解密后才能获得结果通知的内容
     * <p>
     * 解密方式<br>
     * 解密步骤如下：
     * （1）对加密串A做base64解码，得到加密串B
     * （2）对商户key做md5，得到32位小写key* ( key设置路径：微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置 )
     * <p>
     * （3）用key*对加密串B做AES-256-ECB解密（PKCS7Padding）
     * <p>
     * 接口链接<br>
     * 在申请退款接口中上传参数“notify_url”以开通该功能
     * 如果链接无法访问，商户将无法接收到微信通知。
     * 通知url必须为直接可访问的url，不能携带参数。公网域名必须为https，如果是走专线接入，使用专线NAT IP或者私有回调域名可使用http。
     * 示例：notify_url：“https://pay.weixin.qq.com/wxpay/pay.action”
     *
     * @param request Request请求
     * @return 通知结果
     */
    public Result<RefundResponse> execNotifyV3(HttpServletRequest request) {
        try {
            // 请求头Wechatpay-Nonce
            String nonce = request.getHeader(WechatPayHttpHeaders.WECHAT_PAY_NONCE);
            // 请求头Wechatpay-Timestamp
            String timestamp = request.getHeader(WechatPayHttpHeaders.WECHAT_PAY_TIMESTAMP);
            // 请求头Wechatpay-Signature
            String signature = request.getHeader(WechatPayHttpHeaders.WECHAT_PAY_SIGNATURE);
            String body = RequestUtil.getStrFromInputSteam(request.getInputStream());
            // 构建request，传入必要参数
            NotificationRequest notificationRequest = new NotificationRequest.Builder().withSerialNumber(wxPayConfig.getMerchantSerialNumber())
                    .withNonce(nonce)
                    .withTimestamp(timestamp)
                    .withSignature(signature)
                    .withBody(body)
                    .build();
            NotificationHandler handler = new NotificationHandler(loadVerifier(), wxPayConfig.getV3Key().getBytes(StandardCharsets.UTF_8));
            // 验签和解析请求体
            Notification notification = handler.parse(notificationRequest);
            log.info("WxNotify::=> 回调通知报文：{}",notification.toString());
            String decryptData = notification.getDecryptData();
            log.info("WxNotify::=> 回调通知业务明文：{}",decryptData);
            RefundNotifyResult notifyResult = JSON.parseObject(decryptData, RefundNotifyResult.class);
            return  Result.tradeSuccess(wrapperRefundResponseV3(notifyResult));
        } catch (Exception e) {
            log.warn("WxNotify::=> 回调时发生异常", e);
        }
        return Result.exception();
    }

    /**
     * 回调成功，同步返回给支付平台的报文
     *
     * @return
     */
    @Override
    public String successMsg() {
        return "<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>";
    }

    /**
     * 回调失败，同步返回给支付平台的报文
     *
     * @return
     */
    @Override
    public String failMsg() {
        return "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[ERROR]]></return_msg></xml>";
    }


    /**
     * 封装退款请求参数
     *
     * @param model 请求参数
     * @return 封装后的请求参数
     */
    private Map<String, String> wrapRefundParam(RefundModel model) {
        Map<String, String> param = new HashMap<>();
        if (StrUtil.isNotEmpty(model.getTradeNo())) {
            param.put("transaction_id", model.getTradeNo());
        }
        if (StrUtil.isNotEmpty(model.getOutTradeNo())) {
            param.put("out_trade_no", model.getOutTradeNo());
        }
        if (StrUtil.isNotEmpty(model.getNotifyUrl())) {
            param.put("notify_url", model.getNotifyUrl());
        }
        param.put("out_refund_no", model.getOutRefundNo());
        param.put("total_fee", String.valueOf(MoneyUtil.yuan2Cent(model.getOrderPrice())));
        param.put("refund_fee", String.valueOf(MoneyUtil.yuan2Cent(model.getRefundAmount())));
        param.put("refund_desc", model.getRefundReason());
        return param;
    }

    /**
     * 封装退款请求参数 V3
     *
     * @param model 请求参数
     * @return V3退款请求参数
     */
    private ApplyRefundModel wapperApplyRefundModel(RefundModel model) {
        ApplyRefundModel refundModel = new ApplyRefundModel();
        refundModel.setOutRefundNo(model.getOutRefundNo());
        refundModel.setTransactionId(model.getTransactionId());
        refundModel.setOutTradeNo(model.getOutTradeNo());
        if (StrUtil.isNotEmpty(model.getRefundReason())) {
            refundModel.setReason(model.getRefundReason());
        }
        if (StrUtil.isNotEmpty(model.getNotifyUrl())) {
            refundModel.setNotifyUrl(model.getNotifyUrl());
        }else {
            refundModel.setNotifyUrl(wxPayConfig.getNotifyurl());
        }
        // TODO 退款资金来源属性设置

        // 金额信息
        RefundAmount amount = new RefundAmount();
        amount.setRefund(MoneyUtil.yuan2Cent(model.getRefundAmount()));
        amount.setTotal(MoneyUtil.yuan2Cent(model.getOrderPrice()));
        amount.setCurrency(CurrencyEnum.CNY.getValue());
        // TODO 退款出资账户及金额
        refundModel.setAmount(amount);
        return refundModel;
    }

    /**
     * 封装查询请求参数
     *
     * @param model 退款请求参数
     * @return 封装后的请求参数
     */
    private Map<String, String> wrapQueryParam(RefundQueryModel model) {
        Map<String, String> param = new HashMap<>();
        if (StrUtil.isNotEmpty(model.getOutRefundNo())) {
            param.put("out_refund_no", model.getOutRefundNo());
        }
        if (StrUtil.isNotEmpty(model.getRefundNo())) {
            param.put("refund_id", model.getRefundNo());
        }
        return param;
    }


    /**
     * 封装退款返回参数(退款下单)
     *
     * @param r 微信返回报文
     * @return 退款结果
     */
    private RefundResponse wrapperRefundResponse(Map<String, String> r) {
        return wrapperRefundResponse(r, WxRefundStateEnum.PROCESSING, true);
    }
    /**
     * 封装退款返回参数(退款下单)V3
     *
     * @param responseModel 微信返回报文
     * @return 退款结果
     */
    private RefundResponse wrapperRefundResponseV3(ApplyRefundResponse responseModel) {
        RefundResponse response = new RefundResponse();
        response.setPayChannel(PayChannel.WX);
        String status = responseModel.getStatus();
        WxRefundStateV3Enum wxRefundState = EnumUtil.getIEnum(WxRefundStateV3Enum.class,status);
        response.setRefundState(wxRefundState.getRefundState());
        response.setNeedQuery(false);
        response.setOutRefundNo(responseModel.getOutRefundNo());
        response.setOutTradeNo(responseModel.getOutTradeNo());
        response.setRefundNo(responseModel.getRefundId());
        response.setTradeNo(responseModel.getTransactionId());
        return response;
    }
    /**
     * 封装退款返回参数(退款下单)V3
     *
     * @param notifyResult 微信返回报文
     * @return 退款结果
     */
    private RefundResponse wrapperRefundResponseV3(RefundNotifyResult notifyResult) {
        RefundResponse response = new RefundResponse();
        response.setPayChannel(PayChannel.WX);
        String status = notifyResult.getRefundStatus();
        WxRefundStateV3Enum wxRefundState = EnumUtil.getIEnum(WxRefundStateV3Enum.class,status);
        response.setRefundState(wxRefundState.getRefundState());
        response.setNeedQuery(false);
        response.setOutRefundNo(notifyResult.getOutRefundNo());
        response.setOutTradeNo(notifyResult.getOutTradeNo());
        response.setRefundNo(notifyResult.getRefundId());
        response.setTradeNo(notifyResult.getTransactionId());
        return response;
    }

    /**
     * 封装退款返回参数
     *
     * @param r             微信返回报文
     * @param wxRefundState 退款状态
     * @return 退款结果
     */
    private RefundResponse wrapperRefundResponse(Map<String, String> r, WxRefundStateEnum wxRefundState, boolean needQuery) {
        RefundResponse response = new RefundResponse();
        response.setPayChannel(PayChannel.WX);
        response.setRefundState(wxRefundState.getRefundState());
        response.setNeedQuery(needQuery);
        response.setOutRefundNo(r.get("out_refund_no"));
        response.setOutTradeNo(r.get("out_trade_no"));
        response.setRefundNo(r.get("refund_id"));
        response.setTradeNo(r.get("transaction_id"));
        return response;
    }
}
